#include "Solver.hpp"
#include "Heuristics.hpp"

Solver::Solver()
{
    this->_nodeChecked = 1;
    this->_maxNodeLoaded = 1;
}

Solver::~Solver()
{

}

void Solver::aStar(Puzzle& p)
{
    std::cout << "--- Lauching A* ---" << std::endl;
    this->_timer.reset();

    Puzzle solution(sqrt(p.getSize()), true);
    AStarNode root(p);
    int score = root.computeCost(solution);
    this->_closedList[root] = score;
    this->_insertChilds(root, solution);
    while (!this->_openedList.empty())
    {
        AStarNode current = this->_openedList.top();
        if (current.getPuzzle() == solution)
        {
            std::cout << "Solution found in: " << this->_timer.getElapsedTime() << " s" << std::endl;
            std::cout << "Checked node: " << this->_nodeChecked << std::endl;
            std::cout << "Max node loaded: " << this->_maxNodeLoaded << std::endl;
            this->_generateSolutionPath(&current);
            this->_displaySolution();
            return;
        }
        this->_openedList.pop();
        this->_closedList[current] = current.getTotalCost();
        auto e = this->_closedList.find(current);
        if (e != this->_closedList.end())
            this->_insertChilds(const_cast<AStarNode&>(e->first), solution);
        int nbNode = this->_openedList.size() + this->_closedList.size();
        if (nbNode > this->_maxNodeLoaded)
            this->_maxNodeLoaded = nbNode;
    }
    std::cout << "No solution :'(" << std::endl;
    std::cout << "Checked node: " << this->_nodeChecked << std::endl;
    std::cout << "Max node loaded: " << this->_maxNodeLoaded << std::endl;
}

void Solver::_generateSolutionPath(AStarNode* n)
{
    this->_timer.reset();
    std::cout << "generating solution path: ";
    while (n != NULL)
    {
        this->_solutionPath.push(*n);
        n = n->getParent();
    }
    std::cout << this->_timer.getElapsedTime() << " s" << std::endl;
}

void Solver::_displaySolution()
{
    this->_timer.reset();
    while (!this->_solutionPath.empty())
    {
        std::cout << this->_solutionPath.top() << std::endl;
        this->_solutionPath.pop();
    }
    std::cout << "displayed in: " << this->_timer.getElapsedTime() << " s" << std::endl;
}

void Solver::_insertChilds(AStarNode& parent, Puzzle const & solution)
{
    Puzzle p;
    p = parent.getPuzzle();
    for (int direction = UP; direction <= LEFT ; direction++)
    {
        if (p.move(direction))
        {
            AStarNode node(p, &parent);

            auto e = this->_closedList.find(node);
            if (e == this->_closedList.end())
            {
                this->_nodeChecked++;
                node.computeCost(solution);
                AStarNode* existing = this->_openedList.find(node);
                if (existing != NULL)
                {
                    if (existing->getTotalCost() > node.getTotalCost())
                    {
                        existing->setParent(node.getParent());
                        existing->setRCost(node.getRCost());
                    }
                }
                else
                    this->_openedList.push(node);
            }
            p = parent.getPuzzle();
        }
    }
}
